/*
    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) 2012  Роман Браун <firdragon76@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


#include <QtCore/QTime>
#include "sudokutable.h"
#include "sudokuitem.h"

SudokuTable::SudokuTable(QObject *parent): QObject(parent), complexity(0.3)
{
	for(int i = 0; i < SS::TABLE_SIZE; i++)
	{
		table.append(QList<SudokuItem>());

		for(int j = 0; j < SS::TABLE_SIZE; j++)
		{
			SudokuItem si(this);
			si.setValue(SS::START_TABLE[i][j]);
			si.setStatus(SS::STATIC);
			table[i].append(si);
		}
	}

	for(int i = 0; i < SS::NUM_BLOCKS; i++)
	{
		bigGorBlocks.append(QList<QList<SudokuItem *> >());

		for(int j = 0; j < SS::NUM_EL_BLOCK; j++)
		{
			bigGorBlocks[i].append(QList<SudokuItem *>());

			for(int k = 0; k < SS::TABLE_SIZE; k++)
			{
				SudokuItem *sig = &(table[i * SS::NUM_EL_BLOCK + j][k]);
				SudokuItem *siv = &(table[k][i * SS::NUM_EL_BLOCK + j]);
				bigGorBlocks[i][j].append(sig);
			}
		}
	}
}

SudokuTable::~SudokuTable()
{

}

void SudokuTable::generate()
{
	resTable.clear();
	quint32 index1, index2, i, j, k;
	qsrand(QTime::currentTime().msec());
	// 1 этап: меняем местами большие строки и столбцы
	getRand(index1, index2, SS::NUM_BLOCKS);
	bigGorBlocks.swap(index1, index2);

	// меняем местами большие столбцы
	getRand(index1, index2, SS::NUM_BLOCKS);

	for(i = 0; i < SS::NUM_BLOCKS; i++)
	{
		for(j = 0; j < SS::NUM_EL_BLOCK; j++)
		{
			for(k = 0; k < SS::NUM_EL_BLOCK; k++)
			{
				bigGorBlocks[i][j].swap(index1 * SS::NUM_EL_BLOCK + k, index2 * SS::NUM_EL_BLOCK + k);
			}
		}
	}

	// 2 этап: меняем местами строки и столбцы в больших строках и столбцах
	for(i = 0; i < SS::NUM_BLOCKS; i++)
	{
		getRand(index1, index2, SS::NUM_EL_BLOCK);
		bigGorBlocks[i].swap(index1, index2);
	}

	// меняем местами столбцы в больших столбцах
	for(i = 0; i < SS::NUM_BLOCKS; i++)
	{
		getRand(index1, index2, SS::NUM_EL_BLOCK);

		for(j = 0; j < SS::NUM_BLOCKS; j++)
		{
			for(k = 0; k < SS::NUM_EL_BLOCK; k++)
			{
				bigGorBlocks[j][k].swap(i * SS::NUM_EL_BLOCK + index1, i * SS::NUM_EL_BLOCK + index2);
			}
		}
	}

	// 3 этап: создаём окончательную таблицу
	for(i = 0; i < SS::NUM_BLOCKS; i++)
	{
		for(j = 0; j < SS::NUM_EL_BLOCK; j++)
		{
			resTable.append(bigGorBlocks.at(i).at(j));
		}
	}

	// 4 этап: генерация игровой таблицы
	generatePlayTable();
	generateBlockTable();
	generateColumnTable();
}

void SudokuTable::setComplexity(const qreal &complexity)
{
	this->complexity = complexity;
}

void SudokuTable::getRand(quint32 &i1, quint32 &i2, quint32 num)
{
	quint32 i = 0;

	while(i < 1000)
	{
		i1 = qrand() % num;
		i2 = qrand() % num;

		if(i1 != i2)
			break;
	}
}

QList< QList< quint16 > > SudokuTable::getResTable() const
{
	QList<QList<quint16> > ret;

	for(int i = 0; i < resTable.count(); i++)
	{
		ret.append(QList<quint16>());

		for(int j = 0; j < resTable.at(i).count(); j++)
		{
			ret[i].append(resTable.at(i).at(j)->getValue());
		}
	}

	return ret;
}

QList< QList< quint16 > > SudokuTable::getGenTable() const
{
	QList<QList<quint16> > ret;

	for(int i = 0; i < resTable.count(); i++)
	{
		ret.append(QList<quint16>());

		for(int j = 0; j < resTable.at(i).count(); j++)
		{
			ret[i].append(resTable.at(i).at(j)->getViewValue());
		}
	}

	return ret;
}

void SudokuTable::generatePlayTable()
{
	quint32 allNum = SS::TABLE_SIZE * SS::TABLE_SIZE;
	quint32 numEmptys = quint32(qreal(allNum) * complexity);
	quint32 numEmptyRow, numEmptyCol;

	for(int i = 0; i < numEmptys; i++)
	{
		numEmptyRow = qrand() % SS::TABLE_SIZE;
		numEmptyCol = qrand() % SS::TABLE_SIZE;

		if(resTable[numEmptyRow][numEmptyCol]->getStatus() == SS::EMPTY)
		{
			numEmptyRow = qrand() % SS::TABLE_SIZE;
			numEmptyCol = qrand() % SS::TABLE_SIZE;
		}

		resTable[numEmptyRow][numEmptyCol]->setStatus(SS::EMPTY);
	}
}

void SudokuTable::generateBlockTable()
{
	resBlockTable.clear();

	for(int i = 0; i < SS::TABLE_SIZE; i++)
	{
		resBlockTable.append(QList<SudokuItem *>());
	}

	for(int i = 0; i < SS::NUM_BLOCKS; i++)
	{
		for(int j = 0; j < SS::NUM_EL_BLOCK; j++)
		{
			for(int k = 0; k < SS::NUM_EL_BLOCK; k++)
			{
				for(int l = 0; l < SS::NUM_EL_BLOCK; l++)
				{
					resBlockTable[i * SS::NUM_BLOCKS + k].append(bigGorBlocks.at(i).at(j).at(l));
				}
			}
		}
	}
}

void SudokuTable::generateColumnTable()
{
	resColumnTable.clear();

	for(int i = 0; i < SS::TABLE_SIZE; i++)
	{
		resColumnTable.append(QList<SudokuItem *>());

		for(int j = 0; j < SS::TABLE_SIZE; j++)
		{
			resColumnTable[i].append(resTable.at(j).at(i));
		}
	}
}

bool SudokuTable::convergence()
{
	bool ret = true;
	int i = 0, j = 0;
	quint16 sum = 0;

	for(i = 0; i < SS::TABLE_SIZE; i++)
	{
		sum = 0;

		for(j = 0; j < SS::TABLE_SIZE; j++)
		{
			sum += resBlockTable.at(i).at(j)->getViewValue();
		}

		if(sum != SS::SUM_CONVERGENCE)
			ret = false;
	}

	for(i = 0; i < SS::TABLE_SIZE; i++)
	{
		sum = 0;

		for(j = 0; j < SS::TABLE_SIZE; j++)
		{
			sum += resColumnTable.at(i).at(j)->getViewValue();
		}

		if(sum != SS::SUM_CONVERGENCE)
			ret = false;
	}

	for(i = 0; i < SS::TABLE_SIZE; i++)
	{
		sum = 0;

		for(j = 0; j < SS::TABLE_SIZE; j++)
		{
			sum += resTable.at(i).at(j)->getViewValue();
		}

		if(sum != SS::SUM_CONVERGENCE)
			ret = false;
	}

	return ret;
}

SudokuItem *SudokuTable::getSudokuItem(quint32 row, quint32 col)
{
	return resTable[row][col];
}

void SudokuTable::clear()
{
	resTable.clear();
	resBlockTable.clear();
	resColumnTable.clear();

	for(int i = 0; i < SS::TABLE_SIZE; i++)
	{
		for(int j = 0; j < SS::TABLE_SIZE; j++)
		{
			table[i][j].setStatus(SS::STATIC);
		}
	}
}

#include "sudokutable.moc"
